import * as React from 'react' ;
import type { ReactText } from 'react' ;
import ReactDOM from 'react-dom' ;
import classNames from 'classnames';
import Notice,{ NoticeProps } from './Notice';
import useNotification from './useNotification' ;

let seed = 0 ;
const now = Date.now();

function getUuid() {
  const id = seed ;
  seed += 1 ; //seed自增1
  return `origin_notification_${now}_${id}` ;
}

export interface NoticeContent extends Omit<NoticeProps,'prefixCls'|'children'|'noticeKey'|'onClose'> {
  prefixCls?: string ;
  key?: React.Key ;
  updateMark?: string ;
  content?: React.ReactNode ;
  onClose?: () => void;
}

export type NoticeFunc = (noticeProps: NoticeContent) => void;
export type HolderReadyCallback = (
  div: HTMLDivElement ,
  noticeProps: NoticeContent,
) => void;

export interface NotificationInstance {
  notice: NoticeFunc;
  removeNotice: (key: React.Key) => void;
  destroy: () => void;
  component: Notification;

  useNotification: () => [NoticeFunc, React.ReactElement] ;
}

export interface NotificationProps {
  prefixCls?: string ;
  className?: string ;
  style?: React.CSSProperties ;
  transitionName?: string ;
  animation?: string | Object ;
  maxCount?: number ;
  closeIcon?: React.ReactNode;
}

interface NotificationState {
  notices: {
    notice: NoticeContent & {
      userPassKey?: React.Key;
    };
    holderCallback?: HolderReadyCallback;
  }[];
}

class Notification extends React.Component<NotificationProps,NotificationState> {
  static newInstance: (
    properties: NotificationProps & { getContainer?: () => HTMLElement },
    callback: (instance: NotificationInstance) => void ,
  ) => void;

  static defaultProps = {
    prefixCls: 'origin-notification' ,
    animation: 'fade' ,
    style: {
      top: 65,
      left: '50%',
    }
  }

  state:NotificationState = {
    notices:[]
  }

  add = (originNotice: NoticeContent, holderCallback?: HolderReadyCallback) => {
    const key = originNotice.key || getUuid();  //add的notice的key
    const notice: NoticeContent & {
      key: React.Key;
      userPassKey?: React.Key
    } = {
      ...originNotice,
      key,
    };
    const { maxCount } = this.props ;
    this.setState((previousState: NotificationState)=>{
      const { notices } = previousState;
      const noticeIndex = notices.map(item=>item.notice.key).indexOf(key);
      const updatedNotices = notices.concat(); //创建一个数组副本
      // updatedNotices.push({ notice,holderCallback });
      if (noticeIndex !== -1) {
        // 如果即将add的notice的key已存在,则替换已存在的notice
        updatedNotices.splice(
          noticeIndex, //从这位开始
          1, //删除一个元素
          {notice, holderCallback}, //插入元素
        );
      }else{
        // 如果即将add的notice的key不存在
        if(maxCount && notices.length >= maxCount){ //且当前notices数组的长度已经到达上限
          // updatedNotices[0]即将被移除,移除前将updatedNotices[0]的notice.key赋予新的notice
          notice.key = updatedNotices[0].notice.key as React.Key ;
          notice.updateMark = getUuid();

          notice.userPassKey = key; //User may close by key directly.

          updatedNotices.shift() ; //移除第一个元素
        }
        updatedNotices.push({ notice, holderCallback }); //在队尾压入新notice
      }
      return {
        notices:updatedNotices,
      }
    });
  }

  // 根据key移除某个notice实例对象
  remove = (removeKey: React.Key) => {
    this.setState(({ notices }: NotificationState)=>({
      notices: notices.filter(({ notice: {key , userPassKey} })=>{
        const mergedKey = userPassKey || key ; //优先取userPassKey的值,若无则取key
        return mergedKey !== removeKey ;
      }),
    }));
  }

  noticePropsMap: Record<
    React.Key, //键
    { //值
      props: NoticeProps & {
        key: ReactText;
      };
      holderCallback?: HolderReadyCallback;
    }
  > = {};

  render() {
    const { prefixCls,className,closeIcon,style } = this.props ;
    const { notices } = this.state ;
    
    const noticeKeys: React.Key[] = [];

    notices.forEach(({ notice, holderCallback }, index) => {
      const updateMark = index === notices.length -1 ? notice.updateMark : undefined ;
      const { key , userPassKey } = notice ;

      const noticeProps= {
        prefixCls ,
        closeIcon ,
        ...notice ,
        ...notice.props,
        key,
        noticeKey: userPassKey || key ,
        updateMark ,
        onClose: (noticeKey: React.Key) => {
          this.remove(noticeKey);
          notice.onClose?.();
        },
        onClick: notice.onClick,
        children: notice.content,
      } as NoticeProps & { key: ReactText };

      noticeKeys.push(key as React.Key) ;
      this.noticePropsMap[key as React.Key] = {props:noticeProps, holderCallback}
    });

    return (
      // au-message
      <div className={classNames(prefixCls, className)} style={style}>
        {
          noticeKeys.map((key)=>{
            const {
              props: noticeProps,
              holderCallback
            } = this.noticePropsMap[key];
            if(holderCallback) {
              return (
                <div>holderCallback...暂未实现...</div>
              );
            }
            return (
              <Notice
                {...noticeProps}
              />
            );
          })
        }
      </div>
    );
  }
}

Notification.newInstance = function newNotificationInstance(properties,callback) {
  const { getContainer, ...props } = properties || {} ;
  const div = document.createElement('div');
  if(getContainer) {
    const root = getContainer();
    root.appendChild(div);
  } else {
    document.body.appendChild(div) ;
  }
  let called = false; //标记位,确保在Notification实例化后只调用一次callback
  function ref(notification:Notification) {
    if(called) {
      return ;
    }
    called = true ;
    callback({
      notice(noticeProps) {
        notification.add(noticeProps);
      },
      removeNotice(key) {
        notification.remove(key) ;
      },
      component: notification,
      destroy() {
        ReactDOM.unmountComponentAtNode(div);
        if(div.parentNode) {
          div.parentNode.removeChild(div);
        }
      } ,

      // Hooks
      useNotification() {
        return useNotification(notification);
      }
    });
  }

  ReactDOM.render(<Notification {...props} ref={ref}/>,div);
}

export default Notification ;